package org.opencare.monitoring;

import java.awt.AWTException;
import java.awt.Button;
import java.awt.CheckboxMenuItem;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.StringReader;
import java.net.URL;
import java.util.Iterator;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;

import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.model.Document;
import org.apache.abdera.parser.Parser;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opencare.lib.model.Constants;
import org.opencare.lib.model.edxl.EDXLDistribution;
import org.opencare.lib.model.ExtensionFactory;
import org.opencare.lib.model.cap.Alert;
import org.opencare.lib.model.cap.Info;
import org.opencare.lib.model.cap.NameValue;

public class OCMon implements Constants {

	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	// Member for jms
	private ActiveMQConnectionFactory activeMQFactory;
	private Connection jmsConn;
	private Session session;	
	private Topic topic;
	private TopicListener topicListener;
	private String jmsUrl;
	private String jmsClientId;
	private String jmsTopic;
	private String jmsUser;
	private String jmsPassword;
	private String soundAlert;
	private String soundNotify;
	
	
	// Member for System Tray Icon
	private TrayIcon trayIcon;
	private SystemTray tray;
	private String trayIconFile;
	private PopupMenu popup;
	private MenuItem item;
	private Frame f;
	private Panel nPanel;
	private TextField username;
	private TextField password;
	private CheckboxMenuItem cb;
	
	// Member for OCLib and Apache Abdera
	private Abdera abdera;
	private Factory abderaFactory;
	private Document<EDXLDistribution> doc;
	private EDXLDistribution edxl;
	private Parser parser;
	
	// Member for common configuration
	private PropertiesConfiguration config;


	// Initialize system property
	void initProperty() {
		try {
			config = new PropertiesConfiguration("usergui.properties");

			// Get user setting jms url, if not set assign a default vaue
			jmsUrl = config.getString("jms.url","tcp://alerts.opencare.org:61616");
			jmsClientId = config.getString("jms.clientId","opencare.mon-1011");
			jmsTopic = config.getString("jms.topic","opencare.messages.ndwc");
			jmsUser  = config.getString("jms.user","user1");
			jmsPassword  = config.getString("jms.password","pass1");
			soundAlert = config.getString("sound.alert","C:/WINDOWS/Media/ringin.wav");
			soundNotify = config.getString("sound.notify","C:/WINDOWS/Media/ringin.wav");
			trayIconFile = config.getString("tray.icon","/oc.gif");
						
			// Set property 
			config.setProperty("jms.url",jmsUrl);
			config.setProperty("jms.clientId", jmsClientId);
			config.setProperty("jms.topic", jmsTopic);
			config.setProperty("jms.user", jmsUser);
			config.setProperty("jms.password", jmsPassword);
			config.setProperty("tray.icon", trayIconFile);
			
			//config.save();
						
			// Initialize ActiveMQ
			activeMQFactory = new ActiveMQConnectionFactory(jmsUrl);

			jmsConn = activeMQFactory.createConnection();
			jmsConn.setClientID(jmsClientId);
			session = jmsConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
			topic = session.createTopic(jmsTopic);
			MessageConsumer consumer = session.createDurableSubscriber(topic,
					topic.getTopicName());

			topicListener = new TopicListener();
			consumer.setMessageListener(topicListener);
			jmsConn.start();
						
			// Initialize Apache Abdera library
			abdera = new Abdera();
			abderaFactory = abdera.getFactory();
			abderaFactory.registerExtension(new ExtensionFactory());

			parser = abdera.getParser();
						
		} catch (ConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	// Start system service
	public void start() {
			
		logger.info("application is starting....");
	
		// Initialize system property		
		initProperty();

		// Exit if system is not support system tray
		if (SystemTray.isSupported() == false) {
			logger.error("No system tray available");
			return;
		}

		// If supported, get system tray object
		tray = SystemTray.getSystemTray();

		// Get tray icon
		URL imgUrl = getClass().getResource(trayIconFile);
		Image image = Toolkit.getDefaultToolkit().getImage(imgUrl);

		// Create a popup menu
		popup = new PopupMenu();
		
		// Create tray
		trayIcon = new TrayIcon(image, "OpenCARE", popup);
		trayIcon.setImageAutoSize(true);

		item = new MenuItem("Properties");
		item.setFont(new Font("Helvetica", Font.BOLD, 12));

		// Create properties frame
		f = new Frame("Properties");
		f.setIconImage(image);

		// North panel
		nPanel = new Panel();
		f.add("North", nPanel);

		GridBagConstraints gc = new GridBagConstraints();
		GridBagLayout gridbag = new GridBagLayout();

		nPanel.setLayout(gridbag);

		// Make components fill the available display area
		// in the horizontal and vertical directions
		gc.fill = GridBagConstraints.BOTH;

		// Create the Label component.
		Label acct = new Label("Account");
		acct.setAlignment(Label.LEFT);

		// Make the Label component the last one in its row.
		gc.gridwidth = GridBagConstraints.REMAINDER;

		gridbag.setConstraints(acct, gc);
		nPanel.add(acct);

		Label uL = new Label("Username");
		uL.setFont(new Font("Helvetica", Font.BOLD, 12));
		uL.setAlignment(Label.CENTER);

		gc.gridwidth = GridBagConstraints.RELATIVE;

		gridbag.setConstraints(uL, gc);
		nPanel.add(uL);

		username = new TextField();
		username.setColumns(20);

		// Make the Label component the last one in its row.
		gc.gridwidth = GridBagConstraints.REMAINDER;

		gridbag.setConstraints(username, gc);
		nPanel.add(username);

		Label pL = new Label("Password");
		pL.setFont(new Font("Helvetica", Font.BOLD, 12));
		pL.setAlignment(Label.CENTER);

		gc.gridwidth = GridBagConstraints.RELATIVE;

		gridbag.setConstraints(pL, gc);
		nPanel.add(pL);

		password = new TextField();
		password.setColumns(20);
		gc.gridwidth = GridBagConstraints.RELATIVE;

		gridbag.setConstraints(password, gc);
		nPanel.add(password);

		// South panel
		Panel sPanel = new Panel();
		f.add("South", sPanel);

		Button b = new Button("Save");
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				f.setVisible(false);
			}
		});
		sPanel.add(b);

		Button c = new Button("Cancel");
		sPanel.add(c);

		f.pack();

		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				f.setVisible(true);
			}
		});
		popup.add(item);

		// Checked
		cb = new CheckboxMenuItem("Event alarm");
		cb.setState(true);
		popup.add(cb);

		// Add separator
		popup.addSeparator();

		// About item
		item = new MenuItem("About");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Show about window

			}
		});
		popup.add(item);

		// Help item
		item = new MenuItem("Help");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Show about window

			}
		});
		popup.add(item);

		// Add separator
		popup.addSeparator();

		// Exit item
		item = new MenuItem("Exit");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Exit application
				try {
					session.close();
					jmsConn.stop();
					jmsConn.close();
					f.dispose();
					tray.remove(trayIcon);
				} catch (JMSException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				// logger.info("Exit application...");
			}
		});

		popup.add(item);

		try {
			tray.add(trayIcon);
		} catch (AWTException e) {
			System.err.println("Can't add to tray");
		}
	}

	public void stop() {
		logger.info("application is stopping....");
	}

	protected void play(String sound) {
		File soundFile = new File(sound);
		int mediaLength = 1000;

		/*
		 * String input = null; input = JOptionPane .showInputDialog("How long
		 * is your alarm sound? (in milli seconds)"); try { mediaLength =
		 * Integer.parseInt(input); } catch (Exception ex) {
		 * System.out.println(input + " is not integer for sound length"); }
		 */

		int alarmWaitInMinutes = 0;

		/*
		 * input = JOptionPane .showInputDialog("How long shall I wait before
		 * alarm? (in minutes)"); try { alarmWaitInMinutes =
		 * Integer.parseInt(input); } catch (Exception ex) {
		 * System.out.println(input + " is not integer for alarm wait time"); }
		 */

		int alarmWaitInMilliSecs = alarmWaitInMinutes * 1000 * 60;
		logger.debug("I will wait " + alarmWaitInMilliSecs
				+ " milli seconds");
		try {
			Thread.sleep(alarmWaitInMilliSecs);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		logger.debug(soundFile.getAbsolutePath());

		try {
			AudioInputStream stream = AudioSystem
					.getAudioInputStream(soundFile);
			AudioFormat format = stream.getFormat();
			DataLine.Info info = new DataLine.Info(Clip.class, stream
					.getFormat(), ((int) stream.getFrameLength() * format
					.getFrameSize()));

			Clip clip;
			clip = (Clip) AudioSystem.getLine(info);

			clip.open(stream);
			clip.start();
			try {
				Thread.sleep(mediaLength);
			} catch (Exception e) {
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public class TopicListener implements MessageListener {
		
		public void onMessage(Message message) {
			// TODO Auto-generated method stub
			try {
				logger.debug("Receive message:" + message.getJMSMessageID());

				String msgText;

				if (message instanceof TextMessage) {
					msgText = ((TextMessage) message).getText();
				} else { // If it is not a TextMessage...
					msgText = message.toString();
				}

				logger.debug(msgText);
				
				StringReader in2 = new StringReader(msgText);
				Document<EDXLDistribution> edxl_doc = parser.parse(in2);

				EDXLDistribution edxl_parsed = (EDXLDistribution) edxl_doc
						.getRoot();

				// Assume XmlContent is CAP
				Alert cap = (Alert) edxl_parsed.getContentObject().get(0)
						.getXmlContent().getEmbeddedXMLContent().get(0)
						.getAlert().get(0);

				Iterator<Info> iInfo = cap.getInfo().iterator();

				String alertLevel = null;

				while (iInfo.hasNext()) {
					Info info = iInfo.next();

					//logger.debug(info.getDescription());
					Iterator<NameValue> inv = info.getParameters().iterator();
					alertLevel = info.getParameter("alertLevel");
					
					trayIcon.displayMessage(alertLevel + " Alert Level", info.getDescription(),TrayIcon.MessageType.INFO);
										
				}

				//logger.debug("content: " + msgText);				
				//if(cap.getStatus() == Alert.Status.Actual ){
					// If checked, play sound
					if((alertLevel.equalsIgnoreCase("green") || alertLevel.equalsIgnoreCase("ADVISORY")) && cb.getState()) play(soundNotify);								
					if((alertLevel.equalsIgnoreCase("orange") || alertLevel.equalsIgnoreCase("WATCH")) && cb.getState()) play(soundNotify);
					if((alertLevel.equalsIgnoreCase("red") || alertLevel.equalsIgnoreCase("WARNING")) && cb.getState()) play(soundAlert);
				//}		
			} catch (JMSException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		OCMon m = new OCMon();		
		m.start();
	}
}
